"""
评标结果处理脚本

功能：
- 输入 SubDetailItem → 调用通义千问 → 生成结构化 JSON → 校验并入库 BidEvaluationResult
"""

from typing import Dict, Iterable, List, Optional
from spider.models import SubDetailItem
from process.ai.unified import chat_with_usage
from deal.models import BidEvaluationResult, BidOpeningRecord, Project
from django.utils import timezone
import json
import re
from datetime import datetime


DEFAULT_SYSTEM_MSG = (
    "你是一名评标结果结构化抽取专家。"
)

DEFAULT_INSTRUCTION = (
    "从页面 HTML 与上下文中识别评标结果的关键信息并结构化抽取。"
    "包含公示日期、原始URL、项目编号、关联开标时间（若有）。"
)

DEFAULT_OUTPUT_FORMAT = (
    "输出 JSON 字段：\n"
    "announcement_date：公示日期（YYYY-MM-DD 或 ISO8601，无法确定则 null）；\n"
    "project_code：项目编号（字符串，用于入库关联，无法确定则 null）；\n"
    "original_url：原始URL；\n"
    "opening_time：开标时间（可选，若提供则用于关联开标记录）。"
)


def _parse_json(text: str) -> Dict:
    if not text:
        return {}
    s = text.strip()
    if s.startswith("```"):
        s = s.split("\n", 1)[1] if "\n" in s else s
        if s.endswith("```"):
            s = s[:-3].strip()
    s = re.sub(r"^\s*<think>[\s\S]*?</think>\s*", "", s)
    m = re.search(r"\{[\s\S]*\}", s)
    if m:
        frag = m.group(0)
        try:
            return json.loads(frag)
        except Exception:
            pass
    try:
        return json.loads(s)
    except Exception:
        return {}


def _parse_datetime(value: str | None) -> Optional[datetime]:
    if not value:
        return None
    value = value.strip()
    for fmt in ("%Y-%m-%d %H:%M:%S", "%Y/%m/%d %H:%M:%S", "%Y-%m-%d", "%Y/%m/%d"):
        try:
            dt = datetime.strptime(value, fmt)
            return timezone.make_aware(dt) if timezone.is_naive(dt) else dt
        except Exception:
            continue
    try:
        dt = datetime.fromisoformat(value)
        return timezone.make_aware(dt) if timezone.is_naive(dt) else dt
    except Exception:
        return None


def build_messages(
    item: SubDetailItem,
    instruction: Optional[str] = None,
    output_format: Optional[str] = None,
    system: Optional[str] = None,
    extra_context: Optional[Dict[str, str]] = None,
    json_payload: Optional[Dict] = None,
) -> List[Dict[str, str]]:
    sys_msg = system or DEFAULT_SYSTEM_MSG
    inst = instruction or DEFAULT_INSTRUCTION
    fmt = output_format or DEFAULT_OUTPUT_FORMAT
    ctx = extra_context or {}
    payload = json_payload if json_payload is not None else {
        "url": item.url,
        "html_content": item.html_content,
    }
    content = (
        "Instruction:\n" + inst + "\n" +
        "OutputFormat:\n" + fmt + "\n" +
        "Context:\n" + json.dumps(ctx, ensure_ascii=False) + "\n" +
        "URL:\n" + item.url + "\n" +
        "JSON:\n" + json.dumps(payload, ensure_ascii=False)
    )
    return [
        {"role": "system", "content": sys_msg},
        {"role": "user", "content": content},
    ]


def process_evaluation_result(
    item: SubDetailItem,
    instruction: Optional[str] = None,
    output_format: Optional[str] = None,
    system: Optional[str] = None,
    extra_context: Optional[Dict[str, str]] = None,
    model: str = "qwen-plus",
    stream: bool = False,
    json_payload: Optional[Dict] = None,
    **kwargs,
) -> str | Iterable[str]:
    messages = build_messages(item, instruction, output_format, system, extra_context, json_payload=json_payload)
    return chat_with_usage(messages=messages, model=model, **kwargs).get("content")


def process_and_ingest_evaluation_result(
    item: SubDetailItem,
    instruction: Optional[str] = None,
    output_format: Optional[str] = None,
    system: Optional[str] = None,
    extra_context: Optional[Dict[str, str]] = None,
    model: str = "qwen-plus",
    json_payload: Optional[Dict] = None,
    **kwargs,
) -> Dict[str, object]:
    r = chat_with_usage(
        messages=build_messages(
            item=item,
            instruction=instruction,
            output_format=output_format,
            system=system,
            extra_context=extra_context,
            json_payload=json_payload,
        ),
        model=model,
        **kwargs,
    )
    payload = _parse_json(r.get("content") or "")
    errors: List[str] = []
    if not payload:
        return {"ok": False, "errors": ["结构化输出不是合法 JSON"]}

    ann_date = _parse_datetime(payload.get("announcement_date"))
    if not ann_date:
        errors.append("公示日期解析失败：announcement_date")

    original_url = payload.get("original_url") or item.url
    project_code = payload.get("project_code") or f"AUTO-{item.task_id}-{item.id}"
    project = Project.objects.filter(project_code=project_code).first()
    if not project:
        project = Project.objects.create(
            project_code=project_code,
            project_name=payload.get("project_name") or "未命名项目",
            owner_name="未知业主",
            proxy_company="",
            project_type="",
            service_period="",
            budget_amount=None,
            extra_data={},
            crawl_task=item.task,
        )

    opening_time = _parse_datetime(payload.get("opening_time"))
    opening_record = None
    if opening_time:
        opening_record = BidOpeningRecord.objects.filter(opening_time=opening_time, project=project).first()

    existing = BidEvaluationResult.objects.filter(announcement_date=ann_date, project=project).first()
    if existing:
        existing.original_url = original_url
        existing.opening_record = opening_record
        existing.crawl_task_id = item.task_id
        existing.save()
        if item.task_id and item.task:
            item.task.status = "crawled"
            item.task.crawled_at = timezone.now()
            item.task.save(update_fields=["status", "crawled_at"]) 
        return {
            "ok": True,
            "id": existing.id,
            "action": "updated",
            "summary": {
                "url": original_url,
                "project_name": project.project_name,
                "units": [],
                "amount": None,
            },
            "usage": r.get("usage") or {},
        }

    created = BidEvaluationResult.objects.create(
        announcement_date=ann_date,
        original_url=original_url,
        project=project,
        opening_record=opening_record,
        crawl_task_id=item.task_id,
    )
    if item.task_id and item.task:
        item.task.status = "crawled"
        item.task.crawled_at = timezone.now()
        item.task.save(update_fields=["status", "crawled_at"]) 
    return {
        "ok": True,
        "id": created.id,
        "action": "created",
        "summary": {
            "url": original_url,
            "project_name": project.project_name,
            "units": [],
            "amount": None,
        },
        "usage": r.get("usage") or {},
    }


__all__ = [
    "build_messages",
    "process_evaluation_result",
    "process_and_ingest_evaluation_result",
]